
#if 0
 / {
      watchdog_demo: watchdog_demo {
        compatible = "watchdog_demo";
        interrupt-parent = <&ap_gpio>;
        interrupts = <121 IRQ_TYPE_FALLING>;
        signal-gpios = <&ap_gpio 52 GPIO_ACTIVE_HIGH>;
      };
	  
			watchdog_demo: watchdog_demo {
		        compatible = "watchdog_demo";
		       // interrupt-parent = <&ap_gpio>;
		       // interrupts = <121 1>;  // IRQ_TYPE_EDGE_FALLING
		        interrupt-parent = <&eic_debounce>;
		        interrupts = <11 1>;  // IRQ_TYPE_EDGE_FALLING
		        signal-gpios = <&ap_gpio 62 GPIO_ACTIVE_HIGH>;
      		};
 };

 对于SC平台，中断的配置和pinmap是有关系的,当选择ap_gpio时，对于是gpio号，pinmap要配置成gpio功能
 当选择eic_debounce时候，对于的中断号，此时pinmap要配置成EXINT功能
#endif

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>

#define COMPATILE_NAME "watchdog_demo"

static char *events_on[] = {"state=on",NULL};
static char *events_off[] = {"state=off",NULL};

struct watchdog_demo_priv {
	struct device *dev;
	struct delayed_work dwork;
	int irq;
	struct gpio_desc * signal;
};


static void work_func(struct work_struct *work)
{
	struct delayed_work *dwork = to_delayed_work(work);
	struct watchdog_demo_priv *priv = container_of(dwork, struct watchdog_demo_priv, dwork);
	dev_info(priv->dev, "%s\n", __func__);	
//	gpiod_direction_output(priv->signal, 0);
	kobject_uevent_env(&priv->dev->kobj, KOBJ_CHANGE, events_off);
	enable_irq(priv->irq);
}

static irqreturn_t irq_thread(int irq, void *dev)
{
	struct watchdog_demo_priv* priv = dev;
	dev_info(priv->dev, "%s +++\n", __func__);	
	disable_irq_nosync(priv->irq);
	// TODO: report poweroff event 
	schedule_delayed_work(&priv->dwork, msecs_to_jiffies(500));
	dev_info(priv->dev, "%s ---\n", __func__);
	return IRQ_HANDLED;
}

static ssize_t utest_set(struct device *dev,
			       struct device_attribute *attr,
			       const char *buf, size_t count)
{
	#define BUFF_SIZE 32
	int err = 0;
	char data[BUFF_SIZE];
	char* events1[2];
	memset(data, 0, BUFF_SIZE);

	if(count > BUFF_SIZE){
		count = BUFF_SIZE -1;
	}
	strncpy(data, buf, count);
	for(err=0; data[err]; err++){
		if(data[err]=='\n'){
			data[err] = 0; break;
		}
	}
	events1[0] = data;
	events1[1] = NULL;
	err = kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, events1);
	if(err){
		dev_err(dev, "kobject_uevent_env error: %d\n", err);
	}
	return count;
}

static DEVICE_ATTR(utest, 0200, NULL, utest_set);

static int watchdog_demo_probe(struct platform_device *pdev)
{
	struct watchdog_demo_priv *priv;
	struct device_node *np = pdev->dev.of_node;
	int err = 0;
	dev_info(&pdev->dev, "%s enter\n", __func__);
	// store data
	if(NULL==(priv=devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL))){
		dev_err(&pdev->dev, "kzalloc failed\n");
		return -ENOMEM;
	}
	platform_set_drvdata(pdev, priv);
	priv->dev = &pdev->dev;
	priv->irq = irq_of_parse_and_map(np, 0);
	if(!priv->irq){
		dev_err(&pdev->dev, "irq_of_parse_and_map fail\n");
		return -EINVAL;
	}
	INIT_DELAYED_WORK(&priv->dwork, work_func);
	err = devm_request_threaded_irq(&pdev->dev, priv->irq, NULL, irq_thread, IRQF_ONESHOT, COMPATILE_NAME, priv);
	if(err){
		dev_err(&pdev->dev, "devm_request_threaded_irq fail\n");
		return -EINVAL;		
	}
	priv->signal = devm_gpiod_get_optional(&pdev->dev, "signal", GPIOD_OUT_HIGH);
	if (IS_ERR(priv->signal)) {
		err = PTR_ERR(priv->signal);
		dev_err(&pdev->dev, "Getting gpio failed: %d\n", err);
		return err;
	}
	device_create_file(&pdev->dev, &dev_attr_utest);
	kobject_uevent_env(&priv->dev->kobj, KOBJ_ADD, events_on);
	return 0;
}

static int watchdog_demo_remove(struct platform_device *pdev)
{
	struct watchdog_demo_priv *priv = platform_get_drvdata(pdev);
	dev_info(&pdev->dev, "%s enter\n", __func__);
	device_remove_file(&pdev->dev, &dev_attr_utest);
	cancel_delayed_work(&priv->dwork);
	return 0;
}

static void watchdog_demo_shutdown(struct platform_device *pdev)
{
	dev_info(&pdev->dev, "%s enter\n", __func__);
}
static const struct of_device_id watchdog_demo_of_match[] = {
	{ .compatible = COMPATILE_NAME, },
	{},
};

int watchdog_demo_suspend(struct platform_device *pdev, pm_message_t state)
{
	// watchdog_demo watchdog_demo: watchdog_demo_suspend enter, event=2
	// include/linux/pm.h 2ΪPM_EVENT_SUSPEND
	dev_info(&pdev->dev, "%s enter, event=%d\n", __func__, state.event);
	return 0;
}
int watchdog_demo_resume(struct platform_device *pdev)
{
	// watchdog_demo watchdog_demo: watchdog_demo_resume enter
	dev_info(&pdev->dev, "%s enter\n", __func__);
	return 0;
}


static struct platform_driver watchdog_demo_driver = {
	.driver  = {
		.name    = COMPATILE_NAME,
		.owner	= THIS_MODULE,
		.of_match_table = of_match_ptr(watchdog_demo_of_match),

	 },
	.probe   = watchdog_demo_probe,
	.remove  = watchdog_demo_remove,
    .shutdown = watchdog_demo_shutdown,  // system reboot and shutdown
    .suspend = watchdog_demo_suspend,
    .resume = watchdog_demo_resume,

};
#if 1
module_platform_driver(watchdog_demo_driver);
#else
static int __init watchdog_demo_init(void)
{
	pr_info("%s enter\n", __func__);
	platform_driver_register(&watchdog_demo_driver);
	return 0;
}

static void __init watchdog_demo_exit(void)
{
	pr_info("%s enter\n", __func__);
	platform_driver_unregister(&watchdog_demo_driver);
}
late_initcall(watchdog_demo_init);
module_exit(watchdog_demo_exit);
#endif
MODULE_AUTHOR("tj <tj_wust@163.com>");
MODULE_DESCRIPTION("platform demo driver");
MODULE_LICENSE("GPL");
